package com.xiang.server;

import com.xiang.handler.*;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.util.concurrent.DefaultThreadFactory;
import io.netty.util.concurrent.EventExecutorGroup;
import io.netty.util.concurrent.UnorderedThreadPoolEventExecutor;
import lombok.extern.slf4j.Slf4j;

import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;

@Slf4j
public class NettyServer {
    public static void main(String[] args) {
        NettyServer server=new NettyServer();
        server.start(9999);

    }

    private void start(int port) {
        //主从reactor模式
        EventLoopGroup boos =new NioEventLoopGroup(1,new DefaultThreadFactory("boos")); //指定线程池名称
        EventLoopGroup work =new NioEventLoopGroup(Runtime.getRuntime().availableProcessors() * 2 , //线程数：默认cpu核数*2
                    new DefaultThreadFactory("work"));
        //进行相关配置及引导启动
        ServerBootstrap serverBootstrap =new ServerBootstrap();
        //完善线程模型：netty内部实现线程池异步handler调用 ，指定线程池大小及线程名称
        EventExecutorGroup business =new UnorderedThreadPoolEventExecutor(Runtime.getRuntime().availableProcessors() * 2 , new DefaultThreadFactory("business-thread"));
        serverBootstrap.group(boos,work)
                .channel(NioServerSocketChannel.class)//服务端channel
                .option(ChannelOption.SO_BACKLOG,2048) //服务端半连接队列大小
//                .childOption(ChannelOption.SO_KEEPALIVE,true)// 客户端开启keepalive，默认关闭
//                .childOption(ChannelOption.TCP_NODELAY,true) //nagle算法，碎片化数据组成大报文发送，提高效率，默认false
                .handler(new LoggingHandler(LogLevel.INFO)) // 服务端添加handler
                .childHandler(new ChannelInitializer<SocketChannel>() {//客户端添加handler
                    /**
                     * 执行时机：客户端channel初始化执行
                     * @param socketChannel
                     * @throws Exception
                     */
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        ChannelPipeline pipeline = socketChannel.pipeline();
                        //处理客户端发送数据
                        pipeline.addLast(new ServerOutbandHandler1()); //addLast() outband写前面 inband写后面
//                        pipeline.addLast(new ServerInbandHandler1());
//                        pipeline.addLast(new ServerInbandHandler2());
                        /**
                         * netty自带解码器:
                         *  1、换行符 LineBasedFrameDecoder(换行字节大小) 超过长度还为找到换行符则抛异常
                         *  2、自定义分隔符 DelimiterBasedFrameDecoder(字节长度大小，分隔符)
                         *  3、header+body 固定长度解码器  LengthFieldBasedFrameDecoder
                         */
//                        pipeline.addLast(new LineBasedFrameDecoder(65535));
//                        ByteBuf charBuf = socketChannel.alloc().buffer().writeBytes("$".getBytes(StandardCharsets.UTF_8));
//                        pipeline.addLast(new DelimiterBasedFrameDecoder(65535,charBuf));
                        //header+body 固定长度编码器   一次编解码: 帧--> 字节
                        pipeline.addLast(new LengthFieldPrepender(4));
                        //header+body 固定长度解码器   一次编解码：帧--> 字节
                        pipeline.addLast(new LengthFieldBasedFrameDecoder(
                                Integer.MAX_VALUE,//int maxFrameLength, body最大字节长度
                                0,                //int lengthFieldOffset, length偏移量
                                4,                //int lengthFieldLength, length字节数
                                0,                // int lengthAdjustment, header与body间是否填充字节数
                                4                // int initialBytesToStrip  解码后跳过字节数
                        ));
//                        pipeline.addLast(new ServerSimpleInbandHandler()); //inband

                        //二次编解码：字节--> 对象  开箱即用
                        pipeline.addLast(new StringEncoder()); //字符编码
                        pipeline.addLast(new StringDecoder()); //字符解码

//                        pipeline.addLast(new ProtobufEncoder()); //google protobuf 编码
//                        pipeline.addLast(new ProtobufDecoder(proto对象));//google protobuf 解码
//                        pipeline.addLast(new ServerStringInbandHandler());

                        //完善线程模型：异步调用handler并指定线程名称
                        pipeline.addLast(business,"businessHandler",new ServerStringInbandHandler());

                        //IdleHandler
                        pipeline.addLast(new ServerIdleHandler());



                    }

                });
        try {
            ChannelFuture channelFuture = serverBootstrap.bind(new InetSocketAddress(port)) //绑定端口并启动
                    .sync();// 同步操作，绑定好端口后往下执行
            channelFuture.channel().closeFuture().sync(); //监听服务端的端口关闭，优雅释放资源动作
        } catch (InterruptedException e) {
           log.error("NettyServer error:{}",e);
        }finally {
            //释放 线程池资源
            boos.shutdownGracefully();
            work.shutdownGracefully();
        }

    }
}
